package com.ywu.application.standard.redisCache.service;

import com.ywu.application.standard.api.dto.ApiRouterDto;
import com.ywu.application.standard.api.entity.ApiRouterEntity;
import com.ywu.application.standard.api.service.ApiRouterService;
import com.ywu.boot.exception.YwuRuntimeException;
import com.ywu.boot.utils.RedisUtils;
import com.ywu.common.gson.GsonUtils;
import com.ywu.common.result.CommonResult;
import com.ywu.common.utils.TimerIdGenerateUtil;
import com.ywu.application.standard.redisCache.data.YwuOverviewRepository;
import com.ywu.application.standard.redisCache.entity.ApiVisitLogEntity;
import com.ywu.application.standard.redisCache.entity.ApiVisitOverviewEntity;
import com.ywu.application.standard.redisCache.entity.OverViewDataEntity;
import com.ywu.application.standard.redisCache.entity.OverViewGlobalEntity;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName YwuOverviewService
 * @Description TODO
 * @Author GroundDemo
 * @Date 2025/3/24 12:24
 * @Version 1.0
 **/
@Service
@Slf4j
public class YwuOverviewService {

    @Autowired
    private YwuOverviewRepository repository;

    @Autowired
    private ApiRouterService apiRouterService;

    @Autowired
    private RedissonClient redissonClient;

    private static final String redisLockKey = "YWU_OVERVIEW_REDIS_LOCK";

    // 日活
    private static final String REDIS_DAY_PV_KEY = "YWU_PV_KEY:DAY_GROUP:";

    // 月活
    private static final String REDIS_MONTH_PV_KEY = "YWU_PV_KEY:MONTH_GROUP:";

    private static final String REDIS_DAY_UV_KEY = "YWU_UV_KEY:DAY_GROUP:";

    // 月活
    private static final String REDIS_MONTH_UV_KEY = "YWU_UV_KEY:MONTH_GROUP:";

    /**
    * @Author GroundDemo
    * @Description 每隔1小时执行一次，查询当天所有的日志，并进行汇总
    * @Date 13:06 2025/3/24
    * @Param []
    * @return void
    **/
    @Scheduled(cron = "0 0 * * * ?")
    public void resetVisitOverview() {
        // 分布式时只需要执行一次即可，无需都执行，使用分布式锁的方式
        RLock lock = redissonClient.getLock(redisLockKey);
        try {
            boolean lockRes = lock.tryLock(60 * 5, 60 * 10, TimeUnit.SECONDS);
            if (lockRes) {
                Date date = new Date();
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
                String format = simpleDateFormat.format(date);
                // 获取成功，执行逻辑；获取失败，则表示当前有服务正在执行，当前服务无需再次操作
                List<ApiVisitLogEntity> apiVisitLogEntities = repository.queryLogByCurrentTime(format);
                if (CollectionUtils.isEmpty(apiVisitLogEntities)) {
                    return;
                }
                // 构建对象
                Map<String, ApiVisitOverviewEntity> entityMap = doBuildOverview(apiVisitLogEntities, format);
                // 数据入库
                doRepository(entityMap, format);
                // 删除数据
                repository.deleteLongTimeLog(format);
            }
        } catch (Exception e) {
            log.error("resetVisitOverview error {}", e.getMessage());
            throw new YwuRuntimeException("resetVisitOverview error");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
    * @Author GroundDemo
    * @Description 根据查询出的所有结果进行整理
    * @Date 13:48 2025/3/24
    * @Param [apiVisitLogEntities, date]
    * @return java.util.Map<java.lang.String,com.ywu.redis.cache.standard.entity.ApiVisitOverviewEntity>
    **/
    private Map<String, ApiVisitOverviewEntity> doBuildOverview(List<ApiVisitLogEntity> apiVisitLogEntities, String date) {
        // 存储构建后的结果
        Map<String, ApiVisitOverviewEntity> entityHashMap = new HashMap<>();
        for (ApiVisitLogEntity entity : apiVisitLogEntities) {
            if (entityHashMap.containsKey(entity.getUrl())) {
                // 重复值，添加值就可以
                ApiVisitOverviewEntity apiVisitOverviewEntity = entityHashMap.get(entity.getUrl());
                apiVisitOverviewEntity.setTotalVisitCount(apiVisitOverviewEntity.getTotalVisitCount() + 1);
                // 计算totalTime
                BigDecimal totalTime = new BigDecimal(apiVisitOverviewEntity.getTotalTime());
                BigDecimal currentExecTime = new BigDecimal(entity.getTime());
                BigDecimal add = totalTime.add(currentExecTime);
                apiVisitOverviewEntity.setTotalTime(add.toString());
                if (Objects.equals(entity.getStatusCode(), "200")) {
                    apiVisitOverviewEntity.setSuccessCount(apiVisitOverviewEntity.getSuccessCount() + 1);
                } else {
                    apiVisitOverviewEntity.setErrorCount(apiVisitOverviewEntity.getErrorCount() + 1);
                }
            } else {
                // map中不存在，创建初始ApiVisitOverviewEntity对象
                ApiVisitOverviewEntity apiVisitOverviewEntity = new ApiVisitOverviewEntity();
                apiVisitOverviewEntity.setId(TimerIdGenerateUtil.nextId());
                apiVisitOverviewEntity.setMethod(entity.getMethod());
                apiVisitOverviewEntity.setTotalVisitCount(1);
                apiVisitOverviewEntity.setDay(date.split(" ")[0]);
                apiVisitOverviewEntity.setUrl(entity.getUrl());
                apiVisitOverviewEntity.setTotalTime(String.valueOf(entity.getTime()));
                if (Objects.equals(entity.getStatusCode(), "200")) {
                    apiVisitOverviewEntity.setSuccessCount(1);
                    apiVisitOverviewEntity.setErrorCount(0);
                } else {
                    apiVisitOverviewEntity.setErrorCount(1);
                    apiVisitOverviewEntity.setSuccessCount(0);
                }
                apiVisitOverviewEntity.createInfo("system");
                apiVisitOverviewEntity.updateInfo("system");
                entityHashMap.put(entity.getUrl(), apiVisitOverviewEntity);
            }
        }
        return entityHashMap;
    }

    /**
     * @Author GroundDemo
     * @Description 插入数据库
     * @Date 13:49 2025/3/24
     * @Param [entityMap, format]
     * @return void
     **/
    private void doRepository(Map<String, ApiVisitOverviewEntity> entityMap, String format) {
        // 根据url和day信息查询数据库，判断当前数据是否已经入过数据库
        String day = format.split(" ")[0];
        Set<String> keys = entityMap.keySet();
        List<ApiVisitOverviewEntity> existEntityList = repository.queryOverviewListBatch(day, keys);
        if (CollectionUtils.isEmpty(existEntityList)) {
            existEntityList = new ArrayList<>();
        }
        // 存储待插入的数据
        List<ApiVisitOverviewEntity> insertList = new ArrayList<>();
        // 存储待更新的数据
        List<ApiVisitOverviewEntity> updateList = new ArrayList<>();
        Map<String, ApiVisitOverviewEntity> existMap = existEntityList.stream().collect(Collectors.toMap(ApiVisitOverviewEntity::getUrl, res -> res, (v1, v2) -> v1));
        for (String key : keys) {
            if (existMap.containsKey(key)) {
                // 待更新的数据
                ApiVisitOverviewEntity existEntity = existMap.get(key);
                ApiVisitOverviewEntity newEntity = entityMap.get(key);
                existEntity.setTotalVisitCount(existEntity.getTotalVisitCount() + newEntity.getTotalVisitCount());
                existEntity.setSuccessCount(existEntity.getSuccessCount() + newEntity.getSuccessCount());
                existEntity.setErrorCount(existEntity.getErrorCount() + newEntity.getErrorCount());
                BigDecimal oldTotal = new BigDecimal(existEntity.getTotalTime());
                BigDecimal newTotal = new BigDecimal(newEntity.getTotalTime());
                BigDecimal add = oldTotal.add(newTotal);
                BigDecimal divide = add.divide(new BigDecimal(existEntity.getTotalVisitCount()), RoundingMode.FLOOR);
                existEntity.setTotalTime(add.toString());
                existEntity.setAverageTime(Integer.parseInt(divide.toString()));
                updateList.add(existEntity);
            } else {
                // 待插入的数据
                ApiVisitOverviewEntity newEntity = entityMap.get(key);
                BigDecimal totalTime = new BigDecimal(newEntity.getTotalTime());
                BigDecimal divide = totalTime.divide(new BigDecimal(newEntity.getTotalVisitCount()), RoundingMode.FLOOR);
                newEntity.setAverageTime(Integer.parseInt(divide.toString()));
                insertList.add(newEntity);
            }
        }
        // 执行插入
        doInsertList(insertList);
        // 执行修改
        doUpdateList(updateList);
    }

    /**
    * @Author GroundDemo
    * @Description 批量插入
    * @Date 14:12 2025/3/24
    * @Param [insertList]
    * @return void
    **/
    private void doInsertList(List<ApiVisitOverviewEntity> insertList) {
        if (CollectionUtils.isEmpty(insertList)) {
            return;
        }
        // 插入数据需要应用名称，所以需要批量根据url查询服务名称
        List<ApiRouterDto> apiRouterDtos = new ArrayList<>();
        insertList.forEach(res -> {
            ApiRouterDto apiRouterDto = new ApiRouterDto();
            apiRouterDto.setCode(res.getUrl().substring(1));
            apiRouterDtos.add(apiRouterDto);
        });
        CommonResult<List<ApiRouterEntity>> result = apiRouterService.queryApiByCode(apiRouterDtos);
        if (Objects.isNull(result) || !result.getCode().equals("200")) {
            log.error("doInsertList query api info error {}", GsonUtils.serialize(result));
            throw new YwuRuntimeException("doInsertList query api info error ");
        }
        List<ApiRouterEntity> data = result.getData();
        Map<String, ApiRouterEntity> resMap = data.stream().collect(Collectors.toMap(ApiRouterEntity::getCode, res -> res, (v1, v2) -> v1));
        for (ApiVisitOverviewEntity entity : insertList) {
            String code = entity.getUrl().substring(1);
            if (resMap.containsKey(code)) {
                entity.setApplicationName(resMap.get(code).getApplicationName());
            } else {
                entity.setApplicationName("NONE");
            }
        }
        // 插入数据
        repository.insertListBatch(insertList);
    }

    /**
    * @Author GroundDemo
    * @Description 批量修改
    * @Date 14:12 2025/3/24
    * @Param [updateList]
    * @return void
    **/
    private void doUpdateList(List<ApiVisitOverviewEntity> updateList) {
        if (CollectionUtils.isEmpty(updateList)) {
            return;
        }
        updateList.forEach(res -> {
            res.updateInfo("system");
            repository.updateModel(res);
        });
    }

    /**
    * @Author GroundDemo
    * @Description 首页PV、UV、接口访问情况查询
    * @Date 16:34 2025/3/24
    * @Param []
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> overView() {
        // 查询PV - 日活
        List<OverViewGlobalEntity> pvDayList = queryRedisEntityList("PV日活", true, false, REDIS_DAY_PV_KEY);
        // 查询PV - 月活
        List<OverViewGlobalEntity> pvMonthList = queryRedisEntityList("PV月活", false, false, REDIS_MONTH_PV_KEY);
        // 查询UV - 日活
        List<OverViewGlobalEntity> uvDayList = queryRedisEntityList("UV日活", true, true, REDIS_DAY_UV_KEY);
        // 查询UV - 日月活活
        List<OverViewGlobalEntity> uvMonthList = queryRedisEntityList("UV月活", false, true, REDIS_MONTH_UV_KEY);
        // 查询接口信息
        List<OverViewGlobalEntity> apiVisitList = queryApiVisitList();
        // 构建返回值
        Map<String, List<OverViewGlobalEntity>> res = new HashMap<>();
        res.put("pvDayList", pvDayList);
        res.put("pvMonthList", pvMonthList);
        res.put("uvDayList", uvDayList);
        res.put("uvMonthList", uvMonthList);
        res.put("apiVisitList", apiVisitList);
        return CommonResult.success(res);
    }

    /**
    * @Author GroundDemo
    * @Description 查询接口信息
    * @Date 20:38 2025/3/24
    * @Param []
    * @return java.util.List<com.ywu.redis.cache.standard.entity.OverViewGlobalEntity>
    **/
    private List<OverViewGlobalEntity> queryApiVisitList() {
        LocalDateTime currentDate = LocalDateTime.now();
        LocalDateTime searchDate = currentDate.minusDays(6);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("Asia/Shanghai"));
        String format = searchDate.atZone(ZoneId.systemDefault()).format(formatter);
        List<ApiVisitOverviewEntity> entityList = repository.queryOverviewGroupInfo(format);
        Map<String, ApiVisitOverviewEntity> entityMap = entityList.stream()
                .collect(Collectors.toMap(ApiVisitOverviewEntity::getDay, res -> res, (v1, v2) -> v1));
        // 构建返回结果
        List<OverViewGlobalEntity> res = new ArrayList<>();
        // 访问总量
        OverViewGlobalEntity totalEntity = new OverViewGlobalEntity();
        List<OverViewDataEntity> totalDataEntityArrayList = new ArrayList<>();
        totalEntity.setName("API访问总量");
        totalEntity.setData(totalDataEntityArrayList);
        // 访问失败数量
        OverViewGlobalEntity errorEntity = new OverViewGlobalEntity();
        List<OverViewDataEntity> errorDataEntityArrayList = new ArrayList<>();
        errorEntity.setName("API访问失败数量");
        errorEntity.setData(errorDataEntityArrayList);
        // 获取要查询的key
        List<String> keys = getKeys("", true, 9);
        for (String key : keys) {
            if (entityMap.containsKey(key)) {
                ApiVisitOverviewEntity overviewEntity = entityMap.get(key);
                // 总量
                OverViewDataEntity totalTempEntity = new OverViewDataEntity();
                ArrayList<String> totalValue = new ArrayList<>();
                totalValue.add(key.replace("-", "/"));
                totalValue.add(String.valueOf(overviewEntity.getTotalVisitCount()));
                totalTempEntity.setValue(totalValue);
                totalDataEntityArrayList.add(totalTempEntity);
                // 失败数量
                OverViewDataEntity errorTempEntity = new OverViewDataEntity();
                ArrayList<String> errorValue = new ArrayList<>();
                errorValue.add(key.replace("-", "/"));
                errorValue.add(String.valueOf(overviewEntity.getErrorCount()));
                errorTempEntity.setValue(errorValue);
                errorDataEntityArrayList.add(errorTempEntity);
            } else {
                // 总量
                OverViewDataEntity totalTempEntity = new OverViewDataEntity();
                ArrayList<String> totalValue = new ArrayList<>();
                totalValue.add(key.replace("-", "/"));
                totalValue.add("0");
                totalTempEntity.setValue(totalValue);
                totalDataEntityArrayList.add(totalTempEntity);
                // 失败数量
                OverViewDataEntity errorTempEntity = new OverViewDataEntity();
                ArrayList<String> errorValue = new ArrayList<>();
                errorValue.add(key.replace("-", "/"));
                errorValue.add("0");
                errorTempEntity.setValue(errorValue);
                errorDataEntityArrayList.add(errorTempEntity);
            }
        }
        res.add(totalEntity);
        res.add(errorEntity);
        return res;
    }

    /**
    * @Author GroundDemo
    * @Description 查询缓存数据
    * @Date 19:37 2025/3/24
    * @Param [name, isDay, isHyperLog, key]
    * @return java.util.List<com.ywu.redis.cache.standard.entity.OverViewGlobalEntity>
    **/
    private List<OverViewGlobalEntity> queryRedisEntityList(String name, boolean isDay, boolean isHyperLog, String key) {
        List<OverViewGlobalEntity> res = new ArrayList<>();
        OverViewGlobalEntity entity = new OverViewGlobalEntity();
        entity.setName(name);
        List<OverViewDataEntity> dataEntityArrayList = new ArrayList<>();
        entity.setData(dataEntityArrayList);
        // 查询近7天的日活 - 构建近7天的key
        List<String> keys = getKeys(key, isDay, 6);
        log.debug("queryRedisEntityList keys {}", GsonUtils.serialize(keys));
        List<String> redisInfo = null;
        if (isHyperLog) {
            redisInfo = RedisUtils.getHyperLogBatch(keys);
        } else {
            redisInfo = RedisUtils.getByKeys(keys);
        }
        // 构建对象
        for (int index = 0; index < keys.size(); index++) {
            OverViewDataEntity overViewDataEntity = new OverViewDataEntity();
            List<String> value = new ArrayList<>();
            String temp = keys.get(index).replace(key, "");
            temp = temp.replace("-", "/");
            value.add(temp);
            String v = redisInfo.get(index);
            if (Objects.isNull(v)) {
                value.add("0");
            } else {
                value.add(v);
            }
            overViewDataEntity.setValue(value);
            dataEntityArrayList.add(overViewDataEntity);
        }
        res.add(entity);
        return res;
    }

    /**
    * @Author GroundDemo
    * @Description 获取日期的key
    * @Date 20:52 2025/3/24
    * @Param [key, isDay, num]
    * @return java.util.List<java.lang.String>
    **/
    private List<String> getKeys(String key, boolean isDay, int num) {
        DateTimeFormatter formatter = null;
        if (isDay) {
            formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("Asia/Shanghai"));
        } else {
            formatter = DateTimeFormatter.ofPattern("yyyy-MM").withZone(ZoneId.of("Asia/Shanghai"));
        }
        List<String> keys = new ArrayList<>();
        // 获取当前日期
        LocalDateTime currentDate = LocalDateTime.now();
        String format = currentDate.atZone(ZoneId.systemDefault()).format(formatter);
        keys.add(key + format);
        // 往前遍历所有日趋
        for (int index = 0; index < num; index++) {
            LocalDateTime tempDate = null;
            if (isDay) {
                tempDate = currentDate.minusDays(index + 1);
            } else {
                tempDate = currentDate.minusMonths(index + 1);
            }
            String formatKey = tempDate.atZone(ZoneId.systemDefault()).format(formatter);
            keys.add(key + formatKey);
        }
        return keys;
    }

    /**
    * @Author GroundDemo
    * @Description 当日访问总量top10
    * @Date 21:11 2025/3/24
    * @Param []
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> overViewApiVisitTop10() {
        LocalDateTime currentDate = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("Asia/Shanghai"));
        String format = currentDate.atZone(ZoneId.systemDefault()).format(formatter);
        List<ApiVisitOverviewEntity> entityList = repository.overViewApiVisitTop10(format);
        return CommonResult.success(entityList);
    }
}
